package com.teamsun.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.teamsun.annotation.PrivateHandlerMethod;
import com.teamsun.cache.CacheManager;
import com.teamsun.entity.AccessIn;
import com.teamsun.entity.DBCfg;
import com.teamsun.entity.TableHeader;
import com.teamsun.entity.UserInfo;
import com.teamsun.exception.ServiceException;
import com.teamsun.rptcfg.BaseRpt;
import com.teamsun.rptcfg.BaseRptColumn;
import com.teamsun.rptcfg.ParseRptCfg;
import com.teamsun.service.IRptMgrService;
import com.teamsun.service.ISaveRptService;
import com.teamsun.service.ISequenceService;
import com.teamsun.service.IShowRptService;
import com.teamsun.util.Constants;
import com.teamsun.util.RptCfgUtils;
import com.teamsun.util.ReadExcelHeader;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>新增报表配置Controller</p>
 */
@Controller
@RequestMapping("/rptTools/saveRpt")
public class SaveRptController {
    private Logger LOGGER = Logger.getLogger(SaveRptController.class);

    @Autowired
    private ISequenceService sequenceService;

    @Autowired
    private ISaveRptService saveRptService;

    @Autowired
    private IShowRptService showRptService;

    @Autowired
    private IRptMgrService rptMgrService;

    @Autowired
    private CacheManager cacheManager;

    @RequestMapping("/queryDBCfg")
    @ResponseBody
    @PrivateHandlerMethod
    public JSONObject queryDBCfg() {
        Map<String, Object> rptCfgs = saveRptService.queryCfg();
        List<DBCfg> dbCfgs = (List<DBCfg>) rptCfgs.get("dbCfgs");
        List<AccessIn> accessIns = (List<AccessIn>) rptCfgs.get("accessIns");

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("dbCfgs", dbCfgs);
        jsonObject.put("accessIns", accessIns);
        return jsonObject;
    }

    /**
     * 解析报表表头
     */
    @RequestMapping("/parseRptHeader")
    @ResponseBody
    @PrivateHandlerMethod
    public JSONObject parseRptHeader(HttpServletRequest request, @RequestParam("rptHeader") MultipartFile rptHeader) {
        JSONObject jsonObject = new JSONObject();
        try {
            // 获取文件后缀名
            String oriFileName = rptHeader.getOriginalFilename();
            // 文件后缀名
            String fileSuffix = oriFileName.substring(oriFileName.lastIndexOf(".") + 1);

            // 解析Excel表头信息
            ReadExcelHeader readExcelHeader = new ReadExcelHeader();
            List<List<TableHeader>> list = new ArrayList<List<TableHeader>>();
            if(Constants.EXCEL_XLSX.equalsIgnoreCase(fileSuffix)) {
                list = readExcelHeader.parseXlsxExcelHeader(rptHeader.getInputStream());
            } else if(Constants.EXCEL_XLS.equalsIgnoreCase(fileSuffix)) {
                //list = readExcelHeader.parseXlsExcelHeader(rptHeader.getInputStream());
                jsonObject.put("status", "0");
                jsonObject.put("msg", "文件上传失败,请上传xlsx格式的Excel文件");
            } else {
                jsonObject.put("status", "0");
                jsonObject.put("msg", "文件上传失败,只能上传Excel文件");
                return jsonObject;
            }
            // 报表表头信息JSON格式
            String rptHeaderJson = JSON.toJSONString(list);

            // 返回页面信息
            jsonObject.put("status", "1");
            jsonObject.put("rptHeader", rptHeaderJson);
            return jsonObject;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    /**
     * 导入报表配置
     */
    @RequestMapping("/importRptCfg")
    @ResponseBody
    @PrivateHandlerMethod
    public JSONObject importRptCfg(HttpServletRequest request, @RequestParam("rptCfg") MultipartFile rptCfg) {
        JSONObject jsonObject = new JSONObject();
        try {
            // 获取文件后缀名
            String oriFileName = rptCfg.getOriginalFilename();
            // 文件后缀名
            String fileSuffix = oriFileName.substring(oriFileName.lastIndexOf(".") + 1);
            if("xml".equalsIgnoreCase(fileSuffix)) {
                jsonObject.put("status", "1");
            } else {
                jsonObject.put("status", "0");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    /**
     * 下载报表配置模板文件
     * @param request
     */
    @RequestMapping("/downLoadRptTmpleat")
    @PrivateHandlerMethod
    public void downLoadRptTmpleat(HttpServletRequest request, HttpServletResponse response) {
        // 报表表头信息(JSON类型字符串)
        String rptHeaderJson = request.getParameter("rptHeaderInfo");
        String rptName = request.getParameter("rptNameInfo");

        // 根据报表表头信息生成报表配置文件模板(XML格式配置文件)
        Document document = RptCfgUtils.createRptXMLFile(rptHeaderJson, rptName);
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");

        // 将生成的报表配置文件下载
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            XMLWriter xmlWriter = new XMLWriter(outputStream, format);
            xmlWriter.write(document);
            xmlWriter.flush();

            ByteArrayInputStream fis = new ByteArrayInputStream(outputStream.toByteArray());
            String fileName = "rptCfg.xml";
            byte[] bytes = new byte[1024];
            int len = 0;
            OutputStream load = response.getOutputStream();
            response.setBufferSize(512);
            response.setContentType("application/vnd.ms-excel");
            response.addHeader("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            while ((len = fis.read(bytes)) > 0) {
                load.write(bytes, 0, len);
            }
            load.flush();
            load.close();
            fis.close();
            outputStream.close();
            xmlWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 保存报表配置信息
     */
    @RequestMapping("/saveRptCfg")
    @ResponseBody
    @PrivateHandlerMethod
    public JSONObject saveRptCfg(HttpServletRequest request, @RequestParam("rptCfg") MultipartFile rptCfg,
                                 @RequestParam("rptHeader") MultipartFile rptHeader) {
        JSONObject jsonObject = new JSONObject();
        String errroMsg = "";
        try {
            String rptName = request.getParameter("rptName");  //  报表名称
            String dbSource = request.getParameter("dbSource");  // 报表数据源
            String accessIn = request.getParameter("accessIn");  // 报表接入系统
            String token = "";
            if(StringUtils.isNotBlank(accessIn)) {
                // 接入系统Token
                List<AccessIn> accessIns = rptMgrService.queryAccessInByCode(accessIn);
                if(!CollectionUtils.isEmpty(accessIns)) {
                    token = accessIns.get(0).getSysToken();
                }
            }
            String rptHeaderJson = request.getParameter("rptHeaderJson");  // 报表表头
            // 获取报表ID
            int rptId = sequenceService.getRptSequence();

            // 解析报表配置文件
            ParseRptCfg parseRptCfg = new ParseRptCfg();
            BaseRpt rptInfo = parseRptCfg.parseRptCfg(rptCfg.getInputStream(), rptCfg.getOriginalFilename());
            rptInfo.setRptName(rptName);
            rptInfo.setRptId(rptId);
            rptInfo.setDataSource(dbSource);
            rptInfo.setAccessSysCode(accessIn);
            rptInfo.setRptToken(token);

            UserInfo userInfo = (UserInfo) request.getSession().getAttribute(Constants.SESSION_USER);
            rptInfo.setCreateUser(userInfo.getUserId());

            // 重置表头信息(补充表头信息中field、align、width等字段的值)
            List<List<TableHeader>> tableHeaders = RptCfgUtils.resetRptHeader(rptHeaderJson, rptInfo);
            PropertyFilter filter = new PropertyFilter() {
                @Override
                public boolean apply(Object object, String name, Object value) {
                    if((name.equals("sortable") && "false".equals(String.valueOf(value))) || ((name.equals("hidden") && "false".equals(String.valueOf(value)))) || (name.equals("colspan") && "0".equals(String.valueOf(value))) || ((name.equals("rowspan") && "0".equals(String.valueOf(value)))) || ((name.equals("width") && "0".equals(String.valueOf(value))))) {
                        return false;
                    }
                    return true;
                }
            };
            rptHeaderJson = JSON.toJSONString(tableHeaders, filter);
            rptInfo.setRptHeader(rptHeaderJson);

            // 获取顺序Excel表头信息
            ReadExcelHeader readExcelHeader = new ReadExcelHeader();
            InputStream excelInputStream = rptHeader.getInputStream();
            List<String> titleList = readExcelHeader.getXlsxExcelHeaderInfo(excelInputStream, tableHeaders.size());
            rptInfo.setExcelHeader(titleList);

            // 报表配置文件中name属性配置的值必须和Excel表头相同，为了防止用户修改name属性的值，在此再次验证name属性的值
            List<BaseRptColumn> rptCloumns = rptInfo.getRptColumns();
            for(BaseRptColumn rptColumn : rptCloumns) {
                String name = rptColumn.getName();
                if(!titleList.contains(name)) {
                    errroMsg = "xml文件中cloumn元素的name属性值【" + name + "】配置错误，该值无法和Excel表头对应";
                    throw new ServiceException(errroMsg);
                }
            }

            // 将报表配置信息保存至数据库
            saveRptService.saveRptInfo(rptInfo);

            // 将报表表头文件上传至服务器
            // 获取文件后缀名
            String oriRptHeader = rptHeader.getOriginalFilename();
            String fileSuffix = oriRptHeader.substring(oriRptHeader.lastIndexOf(".") + 1);
            // 保存文件路径
            String rptHeaderPath = (String) cacheManager.getCacheValue(Constants.RPTHEADERPATH);
            //String rptHeaderPath = request.getSession().getServletContext().getRealPath("/WEB-INF/rptHeader");
            // 保存文件
            String headerFileName = "RPT_" + rptId + "." + fileSuffix;
            File rptHeaderFile = new File(rptHeaderPath, headerFileName);
            if(!rptHeaderFile.exists()) {
                rptHeaderFile.mkdirs();
            }
            rptHeader.transferTo(rptHeaderFile);

            // 将报表配置文件上传至服务器
            // 获取文件后缀名
            String oriRptCfg = rptCfg.getOriginalFilename();
            // 文件后缀名
            fileSuffix = oriRptCfg.substring(oriRptCfg.lastIndexOf(".") + 1);
            // 保存报表表头文件
            String rptConfigPath = (String) cacheManager.getCacheValue(Constants.RPTCFGPATH);
            //String rptConfigPath = request.getSession().getServletContext().getRealPath("/WEB-INF/rptConfig");

            // 保存文件
            String cfgFileName = "RPT_" + rptId + "." + fileSuffix;
            File rptCfgFile = new File(rptConfigPath, cfgFileName);
            if (!rptCfgFile.exists()) {
                rptCfgFile.mkdirs();
            }
            rptCfg.transferTo(rptCfgFile);
            jsonObject.put("status", "1");

            // 缓存报表配置数据
            cacheManager.addRptCache(String.valueOf(rptId), rptInfo);
        } catch (ServiceException e) {
            errroMsg = e.getMessage();
            jsonObject.put("status", "0");
            jsonObject.put("errorMsg", errroMsg);
        } catch (Exception e) {
            errroMsg = "保存报表失败";
            jsonObject.put("status", "0");
            jsonObject.put("errorMsg", errroMsg);
            e.printStackTrace();
        }
        return jsonObject;
    }

    /**
     * 预览报表SQL
     */
    @RequestMapping("/preViewSQL")
    @ResponseBody
    @PrivateHandlerMethod
    public JSONObject preViewSQL(HttpServletRequest request, @RequestParam("rptCfg") MultipartFile rptCfg) {
        JSONObject jsonObject = new JSONObject();
        String errroMsg = "";
        try {
            String dbSource = request.getParameter("dbSource");  // 报表数据源

            // 解析报表配置文件
            ParseRptCfg parseRptCfg = new ParseRptCfg();
            BaseRpt rptInfo = parseRptCfg.parseRptCfg(rptCfg.getInputStream(), rptCfg.getOriginalFilename());
            rptInfo.setDataSource(dbSource);

            Map<String, String> params = new HashMap<String, String>();

            // 如果是分析型报表，则传递所有维度
            if(Constants.YES.equals(rptInfo.getNeedAnalysis())) {
                List<BaseRptColumn> columns = rptInfo.getRptColumns();
                List<String> rptDimList = new ArrayList<String>();
                for(BaseRptColumn column : columns) {
                    if(Constants.YES.equals(column.getColType())) {
                        rptDimList.add(column.getAliasField());
                    }
                }

                List<BaseRptColumn> hidColumns = rptInfo.getHiddenRptColumns();
                for(BaseRptColumn column : hidColumns) {
                    if(Constants.YES.equals(column.getColType())) {
                        rptDimList.add(column.getAliasField());
                    }
                }

                params.put(Constants.RPTDIM_KEY, StringUtils.join(rptDimList, ","));
            }

            // 获取查询条件列
            List<BaseRptColumn> conditionColumns = rptInfo.getConditionColumns();
            for(BaseRptColumn column : conditionColumns) {
                params.put(column.getAliasField(), "?");
            }

            String querySQL = showRptService.getQuery(rptInfo, params);

            jsonObject.put("querySQL", querySQL);
            jsonObject.put("status", "1");
        } catch (ServiceException e) {
            errroMsg = e.getMessage();
            jsonObject.put("status", "0");
            jsonObject.put("errorMsg", errroMsg);
        } catch (Exception e) {
            errroMsg = "预览报SQL表失败";
            jsonObject.put("status", "0");
            jsonObject.put("errorMsg", errroMsg);
            e.printStackTrace();
        }
        return jsonObject;
    }
}
